/*
 * Copyright (C) 2005 - 2013 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2013 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 - 2013 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORE_MOTIONMASTER_H
#define ARKCORE_MOTIONMASTER_H

#include "Common.h"
#include <vector>
#include "SharedDefines.h"
#include "Object.h"

class MovementGenerator;
class Unit;

// Creature Entry ID used for waypoints show, visible only for GMs
#define VISUAL_WAYPOINT 1

// values 0 ... MAX_DB_MOTION_TYPE-1 used in DB
enum MovementGeneratorType
{
    IDLE_MOTION_TYPE = 0,          // IdleMovementGenerator.h
    RANDOM_MOTION_TYPE = 1,          // RandomMovementGenerator.h
    WAYPOINT_MOTION_TYPE = 2,          // WaypointMovementGenerator.h
    MAX_DB_MOTION_TYPE = 3,          // *** this and below motion types can't be set in DB.
    ANIMAL_RANDOM_MOTION_TYPE = MAX_DB_MOTION_TYPE,          // AnimalRandomMovementGenerator.h
    CONFUSED_MOTION_TYPE = 4,          // ConfusedMovementGenerator.h
    TARGETED_MOTION_TYPE = 5,          // TargetedMovementGenerator.h
    HOME_MOTION_TYPE = 6,          // HomeMovementGenerator.h
    FLIGHT_MOTION_TYPE = 7,          // WaypointMovementGenerator.h
    POINT_MOTION_TYPE = 8,          // PointMovementGenerator.h
    FLEEING_MOTION_TYPE = 9,          // FleeingMovementGenerator.h
    DISTRACT_MOTION_TYPE = 10,          // IdleMovementGenerator.h
    ASSISTANCE_MOTION_TYPE = 11,          // PointMovementGenerator.h (first part of flee for assistance)
    ASSISTANCE_DISTRACT_MOTION_TYPE = 12,          // IdleMovementGenerator.h (second part of flee for assistance)
    TIMED_FLEEING_MOTION_TYPE = 13,          // FleeingMovementGenerator.h (alt.second part of flee for assistance)
    ROTATE_MOTION_TYPE = 14, NULL_MOTION_TYPE = 15,
};

enum MovementSlot
{
    MOTION_SLOT_IDLE, MOTION_SLOT_ACTIVE, MOTION_SLOT_CONTROLLED, MAX_MOTION_SLOT,
};

enum MMCleanFlag
{
    MMCF_NONE = 0, MMCF_UPDATE = 1,          // Clear or Expire called from update
    MMCF_RESET = 2
// Flag if need top()->Reset()
};

enum RotateDirection
{
    ROTATE_DIRECTION_LEFT, ROTATE_DIRECTION_RIGHT,
};

// assume it is 25 yard per 0.6 second
#define SPEED_CHARGE    42.0f

class MotionMaster          //: private std::stack<MovementGenerator *>
{
private:
    //typedef std::stack<MovementGenerator *> Impl;
    typedef MovementGenerator* _Ty;
    _Ty Impl[MAX_MOTION_SLOT];
    bool needInit[MAX_MOTION_SLOT];
    typedef std::vector<_Ty> ExpireList;
    int i_top;

    bool empty () const
    {
        return (i_top < 0);
    }
    void pop ()
    {
        Impl[i_top] = NULL;
        --i_top;
    }
    void push (_Ty _Val)
    {
        ++i_top;
        Impl[i_top] = _Val;
    }

    bool needInitTop () const
    {
        return needInit[i_top];
    }
    void InitTop ();

    void removeEmptyTops ();
public:

    explicit MotionMaster (Unit *unit) :
            i_top(-1), i_owner(unit), m_expList(NULL), m_cleanFlag(MMCF_NONE)
    {
        for (uint8 i = 0; i < MAX_MOTION_SLOT; ++i)
        {
            Impl[i] = NULL;
            needInit[i] = true;
        }
    }
    ~MotionMaster ();

    void Initialize ();
    void InitDefault ();

    int size () const
    {
        return i_top + 1;
    }
    _Ty top () const
    {
        return Impl[i_top];
    }
    _Ty GetMotionSlot (int slot) const
    {
        return Impl[slot];
    }

    void DirectDelete (_Ty curr);
    void DelayedDelete (_Ty curr);

    void UpdateMotion (uint32 diff);
    void Clear (bool reset = true)
    {
        if (m_cleanFlag & MMCF_UPDATE)
        {
            if (reset)
                m_cleanFlag |= MMCF_RESET;
            else
                m_cleanFlag &= ~MMCF_RESET;
            DelayedClean();
        }
        else
            DirectClean(reset);
    }
    void MovementExpired (bool reset = true)
    {
        if (m_cleanFlag & MMCF_UPDATE)
        {
            if (reset)
                m_cleanFlag |= MMCF_RESET;
            else
                m_cleanFlag &= ~MMCF_RESET;
            DelayedExpire();
        }
        else
            DirectExpire(reset);
    }

    void MoveIdle (MovementSlot slot = MOTION_SLOT_ACTIVE);
    void MoveTargetedHome ();
    void MoveRandom (float spawndist = 0.0f);
    void MoveFollow (Unit* target, float dist, float angle, MovementSlot slot = MOTION_SLOT_ACTIVE);
    void MoveChase (Unit* target, float dist = 0.0f, float angle = 0.0f);
    void MoveConfused ();
    void MoveFleeing (Unit* enemy, uint32 time = 0);
    void MovePoint (uint32 id, const Position &pos)
    {
        MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
    }
    void MovePoint (uint32 id, float x, float y, float z);

    // These two movement types should only be used with creatures having landing/takeoff animations
    void MoveLand (uint32 id, Position const& pos, float speed);
    void MoveTakeoff (uint32 id, Position const& pos, float speed);

    void MoveCharge (float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
    void MoveFall (float z, uint32 id = 0);
    void MoveKnockbackFrom (float srcX, float srcY, float speedXY, float speedZ);
    void MoveJumpTo (float angle, float speedXY, float speedZ);
    void MoveJump (float x, float y, float z, float speedXY, float speedZ);
    void MoveSeekAssistance (float x, float y, float z);
    void MoveSeekAssistanceDistract (uint32 timer);
    void MoveTaxiFlight (uint32 path, uint32 pathnode);
    void MoveDistract (uint32 time);
    void MovePath (uint32 path_id, bool repeatable);
    void MoveRotate (uint32 time, RotateDirection direction);

    MovementGeneratorType GetCurrentMovementGeneratorType () const;
    MovementGeneratorType GetMotionSlotType (int slot) const;

    void propagateSpeedChange ();

    bool GetDestination (float &x, float &y, float &z);
private:
    void Mutate (MovementGenerator *m, MovementSlot slot);          // use Move* functions instead

    void DirectClean (bool reset);
    void DelayedClean ();

    void DirectExpire (bool reset);
    void DelayedExpire ();

    Unit *i_owner;
    ExpireList *m_expList;
    uint8 m_cleanFlag;
};
#endif
